package cn.merlulin.monitor.infrastruction.repository;

import cn.merlulin.monitor.domain.model.entity.MonitorDataEntity;
import cn.merlulin.monitor.domain.model.entity.MonitorDataMapEntity;
import cn.merlulin.monitor.domain.model.entity.MonitorFlowDesignEntity;
import cn.merlulin.monitor.domain.model.vo.GatherNodeExpressionVO;
import cn.merlulin.monitor.domain.model.vo.MonitorTreeConfigVO;
import cn.merlulin.monitor.domain.repository.IMonitorRepository;
import cn.merlulin.monitor.infrastruction.dao.*;
import cn.merlulin.monitor.infrastruction.po.*;
import cn.merlulin.monitor.infrastruction.redis.IRedisService;
import cn.merlulin.monitor.types.Constants;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Merlin
 * @version 1.0
 * @email 756151581@qq.com
 * @desc 监控仓储实现类
 * @date 2025/1/16 18:31
 */
@Repository
public class MonitoryRepository implements IMonitorRepository {

    @Resource
    private IMonitorDataDao monitorDataDao;
    @Resource
    private IMonitorDataMapDao monitorDataMapDao;
    @Resource
    private IMonitorDataMapNodeDao monitorDataMapNodeDao;
    @Resource
    private IMonitorDataMapNodeFieldDao monitorDataMapNodeFieldDao;
    @Resource
    private IMonitorDataMapNodeLinkDao monitorDataMapNodeLinkDao;
    @Resource
    private IRedisService redisService;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public List<GatherNodeExpressionVO> queryGatherNodeExpression(String systemName, String className, String methodName) {
        MonitorDataMapNode monitorDataMapNodeReq = new MonitorDataMapNode();
        monitorDataMapNodeReq.setGatherSystemName(systemName);
        monitorDataMapNodeReq.setGatherClazzName(className);
        monitorDataMapNodeReq.setGatherMethodName(methodName);
        List<MonitorDataMapNode> monitorDataMapNodes = monitorDataMapNodeDao.queryMonitorDataMapNode(monitorDataMapNodeReq);
        if (monitorDataMapNodes.isEmpty()) {
            return null;
        }

        List<GatherNodeExpressionVO> gatherNodeExpressionVOS = new ArrayList<>();
        for (MonitorDataMapNode monitorDataMapNode : monitorDataMapNodes) {
            MonitorDataMapNodeField monitorDataMapNodeFieldReq = new MonitorDataMapNodeField();
            monitorDataMapNodeFieldReq.setMonitorId(monitorDataMapNode.getMonitorId());
            monitorDataMapNodeFieldReq.setMonitorNodeId(monitorDataMapNode.getMonitorNodeId());
            List<MonitorDataMapNodeField> monitorDataMapNodeFields = monitorDataMapNodeFieldDao.queryMonitorDataNodeField(monitorDataMapNodeFieldReq);
            List<GatherNodeExpressionVO.Field> fields = new ArrayList<>();
            for (MonitorDataMapNodeField monitorDataMapNodeField : monitorDataMapNodeFields) {
                GatherNodeExpressionVO.Field field = GatherNodeExpressionVO.Field.builder()
                        .logName(monitorDataMapNodeField.getLogName())
                        .logIndex(monitorDataMapNodeField.getLogIndex())
                        .logType(monitorDataMapNodeField.getLogType())
                        .attributeName(monitorDataMapNodeField.getAttributeName())
                        .attributeField(monitorDataMapNodeField.getAttributeField())
                        .attributeOgnl(monitorDataMapNodeField.getAttributeOgnl())
                        .build();
                fields.add(field);
            }
            GatherNodeExpressionVO gatherNodeExpressionVO = GatherNodeExpressionVO.builder()
                    .monitorId(monitorDataMapNode.getMonitorId())
                    .monitorNodeId(monitorDataMapNode.getMonitorNodeId())
                    .monitorNodeName(monitorDataMapNode.getMonitorNodeName())
                    .gatherSystemName(monitorDataMapNode.getGatherSystemName())
                    .gatherClazzName(monitorDataMapNode.getGatherClazzName())
                    .gatherMethodName(monitorDataMapNode.getGatherMethodName())
                    .fields(fields)
                    .build();
            gatherNodeExpressionVOS.add(gatherNodeExpressionVO);
        }
        return gatherNodeExpressionVOS;
    }

    @Override
    public String queryMonitoryNameByMonitorId(String monitorId) {
        return monitorDataMapDao.queryMonitoryNameByMonitorId(monitorId);
    }

    @Override
    public void saveMonitorData(MonitorDataEntity monitorDataEntity) {
        MonitorData monitorData = new MonitorData();
        monitorData.setMonitorId(monitorDataEntity.getMonitorId());
        monitorData.setMonitorName(monitorDataEntity.getMonitorName());
        monitorData.setMonitorNodeId(monitorDataEntity.getMonitorNodeId());
        monitorData.setSystemName(monitorDataEntity.getSystemName());
        monitorData.setClazzName(monitorDataEntity.getClazzName());
        monitorData.setMethodName(monitorDataEntity.getMethodName());
        monitorData.setAttributeName(monitorDataEntity.getAttributeName());
        monitorData.setAttributeField(monitorDataEntity.getAttributeField());
        monitorData.setAttributeValue(monitorDataEntity.getAttributeValue());
        monitorDataDao.insert(monitorData);

        String cacheKey = Constants.RedisKey.monitor_node_data_count_key + monitorDataEntity.getMonitorId() + Constants.UNDERLINE + monitorDataEntity.getMonitorNodeId();
        // 递增监控节点的数据计数
        redisService.incr(cacheKey);
    }

    @Override
    public List<MonitorDataMapEntity> queryMonitorDataMapEntityList() {
        List<MonitorDataMap> monitorDataMaps = monitorDataMapDao.queryMonitorDataMapEntityList();
        List<MonitorDataMapEntity> monitorDataMapEntities = new ArrayList<>(monitorDataMaps.size());
        for (MonitorDataMap monitorDataMap : monitorDataMaps) {
            MonitorDataMapEntity monitorDataMapEntity = MonitorDataMapEntity.builder()
                    .monitorId(monitorDataMap.getMonitorId())
                    .monitorName(monitorDataMap.getMonitorName())
                    .build();
            monitorDataMapEntities.add(monitorDataMapEntity);
        }
        return monitorDataMapEntities;
    }

    @Override
    public MonitorTreeConfigVO queryMonitorFlowData(String monitorId) {
        // 监控节点配置
        List<MonitorDataMapNode> monitorDataMapNodes = monitorDataMapNodeDao.queryMonitorNodeConfigByMonitorId(monitorId);
        // 监控节点链路
        List<MonitorDataMapNodeLink> monitorDataMapNodeLinks = monitorDataMapNodeLinkDao.queryMonitorNodeLinkConfigByMonitorId(monitorId);

        Map<String, List<String>> fromMonitorNodeIdToNodeIds = monitorDataMapNodeLinks.stream()
                .collect(Collectors.groupingBy(MonitorDataMapNodeLink::getFromMonitorNodeId,
                        Collectors.mapping(MonitorDataMapNodeLink::getToMonitorNodeId, Collectors.toList())));

        List<MonitorTreeConfigVO.Node> nodeList = new ArrayList<>();
        for (MonitorDataMapNode monitorDataMapNode : monitorDataMapNodes) {
            // 查询缓存节点流量值
            String cacheKey = Constants.RedisKey.monitor_node_data_count_key + monitorId + Constants.UNDERLINE + monitorDataMapNode.getMonitorNodeId();
            Long count = redisService.getAtomicLong(cacheKey);

            nodeList.add(MonitorTreeConfigVO.Node.builder()
                    .monitorNodeId(monitorDataMapNode.getMonitorNodeId())
                    .monitorNodeName(monitorDataMapNode.getMonitorNodeName())
                    .loc(monitorDataMapNode.getLoc())
                    .color(monitorDataMapNode.getColor())
                    .monitorNodeValue(null == count ? "0" : String.valueOf(count))
                    .build());
        }

        List<MonitorTreeConfigVO.Link> linkList = new ArrayList<>();
        for (MonitorDataMapNodeLink monitorDataMapNodeLink : monitorDataMapNodeLinks) {
            String fromCacheKey = Constants.RedisKey.monitor_node_data_count_key + monitorId + Constants.UNDERLINE + monitorDataMapNodeLink.getFromMonitorNodeId();
            Long fromCacheCount = redisService.getAtomicLong(fromCacheKey);
            Long toCacheCount = 0L;

            List<String> toNodeIds = fromMonitorNodeIdToNodeIds.get(monitorDataMapNodeLink.getFromMonitorNodeId());
            for (String toNodeId : toNodeIds) {
                String toCacheKey = Constants.RedisKey.monitor_node_data_count_key + monitorId + Constants.UNDERLINE + toNodeId;
                toCacheCount += redisService.getAtomicLong(toCacheKey);
            }

            long differenceValue = (null == fromCacheCount ? 0L : fromCacheCount) - toCacheCount;

            linkList.add(MonitorTreeConfigVO.Link.builder()
                    .fromMonitorNodeId(monitorDataMapNodeLink.getFromMonitorNodeId())
                    .toMonitorNodeId(monitorDataMapNodeLink.getToMonitorNodeId())
                    .linkKey(String.valueOf(monitorDataMapNodeLink.getId()))
                    .linkValue(String.valueOf(differenceValue <= 0 ? 0 : differenceValue))
                    .build());

        }
        return MonitorTreeConfigVO.builder()
                .monitorId(monitorId)
                .nodeList(nodeList)
                .linkList(linkList)
                .build();
    }

    @Override
    public List<MonitorDataEntity> queryMonitorDataEntityList(MonitorDataEntity monitorDataEntity) {
        MonitorData monitorDataReq = new MonitorData();
        monitorDataReq.setMonitorId(monitorDataEntity.getMonitorId());
        monitorDataReq.setMonitorName(monitorDataEntity.getMonitorName());
        monitorDataReq.setMonitorNodeId(monitorDataEntity.getMonitorNodeId());
        List<MonitorData> monitorDataList = monitorDataDao.queryMonitorDataList(monitorDataReq);
        List<MonitorDataEntity> monitorDataEntities = new ArrayList<>(monitorDataList.size());
        for (MonitorData monitorData : monitorDataList) {
            MonitorDataEntity monitorDataEntityResp = MonitorDataEntity.builder()
                    .monitorId(monitorData.getMonitorId())
                    .monitorName(monitorData.getMonitorName())
                    .monitorNodeId(monitorData.getMonitorNodeId())
                    .systemName(monitorData.getSystemName())
                    .clazzName(monitorData.getClazzName())
                    .methodName(monitorData.getMethodName())
                    .attributeName(monitorData.getAttributeName())
                    .attributeField(monitorData.getAttributeField())
                    .attributeValue(monitorData.getAttributeValue())
                    .build();
            monitorDataEntities.add(monitorDataEntityResp);
        }
        return monitorDataEntities;
    }

    @Override
    public void updateMonitorFlowDesign(MonitorFlowDesignEntity monitorFlowDesignEntity) {
        transactionTemplate.execute(status -> {
            try {
                List<MonitorFlowDesignEntity.Node> nodeList = monitorFlowDesignEntity.getNodeList();
                for (MonitorFlowDesignEntity.Node node : nodeList) {
                    MonitorDataMapNode monitorDataMapNodeReq = new MonitorDataMapNode();
                    monitorDataMapNodeReq.setMonitorId(monitorFlowDesignEntity.getMonitorId());
                    monitorDataMapNodeReq.setMonitorNodeId(node.getMonitorNodeId());
                    monitorDataMapNodeReq.setLoc(node.getLoc());
                    monitorDataMapNodeDao.updateNodeConfig(monitorDataMapNodeReq);
                }

                List<MonitorFlowDesignEntity.Link> linkList = monitorFlowDesignEntity.getLinkList();
                monitorDataMapNodeLinkDao.deleteLinkFromByMonitorId(monitorFlowDesignEntity.getMonitorId());
                for (MonitorFlowDesignEntity.Link link : linkList) {
                    MonitorDataMapNodeLink monitorDataMapNodeLinkReq = new MonitorDataMapNodeLink();
                    monitorDataMapNodeLinkReq.setMonitorId(monitorFlowDesignEntity.getMonitorId());
                    monitorDataMapNodeLinkReq.setFromMonitorNodeId(link.getFrom());
                    monitorDataMapNodeLinkReq.setToMonitorNodeId(link.getTo());
                    monitorDataMapNodeLinkDao.insert(monitorDataMapNodeLinkReq);
                }
                return 1;
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}
